package com.middlegroup.pay.service.impl;

import com.alibaba.fastjson.JSON;
import com.middlegroup.common.enums.*;
import com.middlegroup.common.exception.ErrorMsg;
import com.middlegroup.common.model.NotifyRequest;
import com.middlegroup.common.model.PayRequest;
import com.middlegroup.common.model.RefundRequestDto;
import com.middlegroup.common.model.TransResult;
import com.middlegroup.pay.entity.*;
import com.middlegroup.pay.service.*;
import com.paypal.api.payments.*;
import com.paypal.base.rest.APIContext;
import com.paypal.base.rest.PayPalRESTException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.math.BigDecimal;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author pc123
 * @since 2019-01-10
 */
@Service
@Slf4j
public class PaypalServiceImpl implements PaypalService {
    @Resource
    private InBillsService inBillsService;
    @Resource
    private ThirdBillsService thirdBillsService;
    @Autowired
    private APIContext apiContext;
    @Resource
    private SystemCfgService systemCfgService;
    @Resource
    private RefundBillsService refundBillsService;
    @Resource
    private NotifyMerchantService notifyMerchantService;
    @Resource
    private InNotifyLogService inNotifyLogService;
    @Resource
    private OutNotifyLogService outNotifyLogService;
    @Override
    public TransResult payPalPay(PayRequest request) {
        TransResult transResult = new TransResult();
        transResult.setBillNo(request.getBillNo());
        transResult.setMerchantBillNo(request.getMerchantBillNo());
        transResult.setAmount(request.getAmount());
        transResult.setTransStatus(0);
        transResult.setTransTime(System.currentTimeMillis());


        ThirdBills thirdBills=new ThirdBills();
        thirdBills.setSubjectCode(request.getSubjectCode());
        thirdBills.setMerchantBillNo(request.getMerchantBillNo());
        thirdBills.setBillNo(request.getBillNo());
        thirdBills.setStatus(0);
        thirdBills.setAmount(request.getAmount());
        thirdBills.setBeginTime(System.currentTimeMillis());
        thirdBills.setCurrency(request.getTransCurrency());//币种
        Amount amount = new Amount();
        amount.setCurrency(request.getTransCurrency());//币种
        amount.setTotal(String.format("%.2f", request.getAmount()));//金额

        Transaction transaction = new Transaction();
        transaction.setDescription(request.getSubject());//备注
        transaction.setAmount(amount);

        List<Transaction> transactions = new ArrayList<>();
        transactions.add(transaction);

        Payer payer = new Payer();
        payer.setPaymentMethod(PaypalPaymentMethod.paypal.toString());

        Payment payment = new Payment();
        payment.setIntent(PaypalPaymentIntent.sale.toString());
        payment.setPayer(payer);
        payment.setTransactions(transactions);
        RedirectUrls redirectUrls = new RedirectUrls();
        String ipPort=systemCfgService.getConfigString(ServiceType.paypal.getCode(), ConfigKeyEnums.ipPort.getName());
        redirectUrls.setCancelUrl(ipPort+"/pay/cancel");//支付取消
        redirectUrls.setReturnUrl(ipPort+"/pay/success");//支付确认地址
        payment.setRedirectUrls(redirectUrls);
        thirdBills.setRequestText(payment.toJSON());//请求体
        try {
            Payment paymentResult = payment.create(apiContext);
            thirdBills.setResponseText(paymentResult.toJSON());//返回体
            if (paymentResult != null) {
                for (Links links : paymentResult.getLinks()) {
                    if (links.getRel().equals("approval_url")) {
                        transResult.setNextStepData(links.getHref());
                    }
                    if (links.getRel().equals("self")) {
                        thirdBills.setRequestUrl(links.getHref());//请求地址
                    }
                }
                transResult.setTransNo(paymentResult.getId());//第三方返回的订单标识
                transResult.setTransStatus(0);
            } else {
                //默认交易处理中
                log.error("调用Paypal支付统一下单接口返回空,billNo:{}", request.getBillNo());
                transResult.setMsg(ErrorMsg.PAY_INTERFACE_RETURN_BLANK);
            }
        } catch (Exception e) {
            thirdBills.setResponseText(e.toString());
        log.error("调用Paypal支付统一下单接口返回空,billNo:{},error:{}",request.getBillNo(),e);
    }
        try {
            thirdBillsService.insert(thirdBills);
        } catch (Exception e) {
            log.error("Paypal支付插入第三方订单表异常,billNo:{},error:{}",request.getBillNo(),e);
        }

        return transResult;
    }
    /**
     * 支付确认
     * @param paymentId
     * @param payerId
     * @return
     * @throws PayPalRESTException
     */
    @Override
    public String executePayment(String paymentId, String payerId){
        String url = "/";
        Payment payment = new Payment();
        payment.setId(paymentId);
        PaymentExecution paymentExecute = new PaymentExecution();
        paymentExecute.setPayerId(payerId);
        ThirdBills thirdBills = new ThirdBills();
        thirdBills.setStatus(0);
        thirdBills.setRequestText(payment.toJSON());//请求体
        thirdBills.setBeginTime(System.currentTimeMillis());
        try {
            Payment paymentResult = payment.execute(apiContext, paymentExecute);
            if (paymentResult != null) {
                InBills inBills = inBillsService.getByOrderId(paymentResult.getId());
                thirdBills.setSubjectCode(inBills.getSubjectCode());
                thirdBills.setMerchantBillNo(inBills.getMerchantBillNo());
                thirdBills.setBillNo(inBills.getBillNo());
                thirdBills.setCurrency(inBills.getCurrency());//币种
                thirdBills.setAmount(inBills.getAmount());

                thirdBills.setRequestText(paymentResult.toJSON());//返回体
                if ("approved".equals(paymentResult.getState())) {
                    inBills.setSaleId(paymentResult.getTransactions().get(0).getRelatedResources().get(0).getSale().getId());
                    thirdBills.setStatus(1);//支付成功
                    inBills.setStatus(1);//支付成功
                    url = inBills.getReturnUrl();
                } else if ("failed".equals(paymentResult.getState())) {
                    thirdBills.setStatus(2);//支付失败
                    inBills.setStatus(2);//支付失败
                }
                try {
                    inBillsService.updateById(inBills);
                } catch (Exception e) {
                    log.error("Paypal支付确认更新第三方订单表t_in_bills状态异常,billNo:{},error:{}", thirdBills.getBillNo(), e);
                }
            }
        } catch (PayPalRESTException e) {
            log.error(e.getMessage());
        }
        try {
            thirdBillsService.insert(thirdBills);
        } catch (Exception e) {
            log.error("Paypal支付确认插入第三方订单表t_third_bills状态异常,billNo:{},error:{}", thirdBills.getBillNo(), e);
        }

        return url;
    }

    /**
     *
     * @return
     * @throws PayPalRESTException
     */
    @Override
    public  Payment payCreditCard() throws PayPalRESTException {
        // ###Address
        // Base Address object used as shipping or billing
        // address in a payment. [Optional]
        Address billingAddress = new Address();
        billingAddress.setCity("Johnstown");
        billingAddress.setCountryCode("US");
        billingAddress.setLine1("52 N Main ST");
        billingAddress.setPostalCode("43210");
        billingAddress.setState("OH");

        // ###CreditCard
        // A resource representing a credit card that can be
        // used to fund a payment.
        CreditCard creditCard = new CreditCard();
        creditCard.setBillingAddress(billingAddress);
        creditCard.setCvv2(874);
        creditCard.setExpireMonth(11);
        creditCard.setExpireYear(2019);
        creditCard.setFirstName("Joe");
        creditCard.setLastName("Shopper");
        creditCard.setNumber("4417119669820331");
        creditCard.setType("visa");

        // ###Details
        // Let's you specify details of a payment amount.
        Details details = new Details();
        details.setShipping("1");
        details.setSubtotal("5");
        details.setTax("1");

        // ###Amount
        // Let's you specify a payment amount.
        Amount amount = new Amount();
        amount.setCurrency("USD");
        // Total must be equal to sum of shipping, tax and subtotal.
        amount.setTotal("8");
        amount.setDetails(details);

        // ###Transaction
        // A transaction defines the contract of a
        // payment - what is the payment for and who
        // is fulfilling it. Transaction is created with
        // a `Payee` and `Amount` types
        Transaction transaction = new Transaction();
        transaction.setAmount(amount);
        transaction
                .setDescription("This is the payment transaction description.");

        // The Payment creation API requires a list of
        // Transaction; add the created `Transaction`
        // to a List
        List<Transaction> transactions = new ArrayList<Transaction>();
        transactions.add(transaction);

        // ###FundingInstrument
        // A resource representing a Payeer's funding instrument.
        // Use a Payer ID (A unique identifier of the payer generated
        // and provided by the facilitator. This is required when
        // creating or using a tokenized funding instrument)
        // and the `CreditCardDetails`
        FundingInstrument fundingInstrument = new FundingInstrument();
        fundingInstrument.setCreditCard(creditCard);

        // The Payment creation API requires a list of
        // FundingInstrument; add the created `FundingInstrument`
        // to a List
        List<FundingInstrument> fundingInstrumentList = new ArrayList<FundingInstrument>();
        fundingInstrumentList.add(fundingInstrument);

        // ###Payer
        // A resource representing a Payer that funds a payment
        // Use the List of `FundingInstrument` and the Payment Method
        // as 'credit_card'
        Payer payer = new Payer();
        payer.setFundingInstruments(fundingInstrumentList);
        payer.setPaymentMethod("credit_card");

        // ###Payment
        // A Payment Resource; create one using
        // the above types and intent as 'sale'
        Payment payment = new Payment();
        payment.setIntent("sale");
        payment.setPayer(payer);
        payment.setTransactions(transactions);
        Payment createdPayment = payment.create(apiContext);
        System.out.print(createdPayment.toString());
        return createdPayment;
    }

    /**
     * 退款
     * @param refundRequest
     * @return
     * @throws PayPalRESTException
     */
    @Override
    public TransResult saleRefund(RefundRequestDto refundRequest,TransResult transResult){
        InBills inBills = inBillsService.getByMerchantBillNo(refundRequest.getMerchantBillNo());
        Sale sale = new Sale();
        sale.setId(inBills.getSaleId());
        Refund refund = new Refund();
        Amount amount = new Amount();
        amount.setCurrency(inBills.getCurrency());//币种
        amount.setTotal(new DecimalFormat("#0.00").format(refundRequest.getRefundAmount()).toString());//金额
        refund.setAmount(amount);
        refund.setReason(refundRequest.getRemark());//退款原因
        ThirdBills thirdBills = new ThirdBills();
        thirdBills.setStatus(0);
        thirdBills.setRequestText(refund.toJSON());//请求体
        thirdBills.setBeginTime(System.currentTimeMillis());
        thirdBills.setMethod("PaypalRefund");
        thirdBills.setSubjectCode(inBills.getSubjectCode());
        thirdBills.setMerchantBillNo(inBills.getMerchantBillNo());
        thirdBills.setBillNo(refundRequest.getBillNo());
        thirdBills.setCurrency(inBills.getCurrency());//币种
        thirdBills.setAmount(refundRequest.getRefundAmount());
        thirdBillsService.insert(thirdBills);
        try {
            Refund re = sale.refund(apiContext, refund);
            log.debug("退款返回======"+re);
            if(re!=null){
                thirdBills.setResponseText(re.toJSON());
                if("completed".equals(re.getState())) {
                    thirdBills.setStatus(1);//支付成功
                    transResult.setTransStatus(1);
                    transResult.setTransNo(re.getId());
                    transResult.setMsg(ErrorMsg.REFUND_SUCCESS);
                    log.debug("退款订单号：{},状态{}", inBills.getSaleId(), re.getState());
                }else{
                    transResult.setMsg(ErrorMsg.REFUND_FAIL);
                    thirdBills.setStatus(2);//退款成功
                    transResult.setTransStatus(2);
                }
            }
        } catch (PayPalRESTException e) {
            log.error("退款失败==="+e.getMessage());
            transResult.setMsg(ErrorMsg.REFUND_FAIL);
            thirdBills.setStatus(2);//退款失败
            transResult.setTransStatus(2);
        }
        try {
            thirdBillsService.updateById(thirdBills);
        } catch (Exception e) {
            log.error("Paypal支付确认插入第三方订单表t_third_bills状态异常,billNo:{},error:{}", thirdBills.getBillNo(), e);
        }
        return transResult;
    }
    /**
     * 查询订单状态
     */
    public  Payment queryPay(String id) throws PayPalRESTException{
        Payment payment = Payment.get(apiContext, id);
        return payment;
    }
    /**
     * 查询订单状态
     */
    public  Refund queryRefund(String id) throws PayPalRESTException{
        Refund payment = Refund.get(apiContext, id);
        return payment;
    }
    public void payPalNotify(HttpServletRequest request){
        Enumeration<String> requestParams = request.getParameterNames();
        String str = "cmd=_notify-validate";
        //遍历获取参数
        try {
            while (requestParams.hasMoreElements()) {
                String param = requestParams.nextElement();
                String value = request.getParameter(param);
                str = str + "&" + param + "=" + URLEncoder.encode(value, "utf-8");
                //此处的编码一定要和自己的网站编码一致，不然会出现乱码，paypal回复的通知为‘INVALID’
                log.debug("ipn回调-----" + param + ":" + value);
            }
            log.debug("paypal传递过来的交易信息:" + str);
            /* 正式环境下
            String payPalUrl = "https://www.paypal.com/cgi-bin/webscr";
            */
            String validateUrl=systemCfgService.getConfigString(ServiceType.paypal.getCode(), ConfigKeyEnums.validateUrl.getName());
            URL u = new URL(validateUrl);
            //正式环境
            //      URL u = new URL("https://www.paypal.com/cgi-bin/webscr");
            URLConnection uc = u.openConnection();
            uc.setDoOutput(true);
            uc.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            PrintWriter pw = new PrintWriter(uc.getOutputStream());
            pw.println(str);
            pw.close();
            //接受 PayPal 对 IPN 回发的回复信息
            BufferedReader in = new BufferedReader(new InputStreamReader(uc.getInputStream()));
            String res = in.readLine();
            in.close();
            String paymentStatus = request.getParameter("payment_status");//交易状态
            String paymentDate = request.getParameter("payment_date");//交易时间
            String paymentAmount = request.getParameter("mc_gross");//交易钱数
            String txnId = request.getParameter("txn_id");//交易id
            log.debug("++++++++paypal回调验证返回:{}",res);
            //…
            //获取 PayPal 对回发信息的回复信息，判断刚才的通知是否为 PayPal 发出的

                //检查付款状态
                //检查 txn_id 是否已经处理过
                //检查 receiver_email 是否是您的 PayPal 账户中的 EMAIL 地址
                //检查付款金额和货币单位是否正确
                //处理其他数据，包括写数据库
                Date date  =new Date(paymentDate);
                if(paymentAmount.contains("-")) {//退款回調（金额为负退款）
                    RefundBills refundBills = refundBillsService.getByOutRequestNo(txnId);
                    OutNotifyLog outNotifyLog = new OutNotifyLog();
                    outNotifyLog.setCallbackText(str);
                    outNotifyLog.setMerchantBillNo(refundBills.getMerchantBillNo());
                    outNotifyLog.setStatus(0);
                    outNotifyLog.setTransNo(txnId);
                    if ("VERIFIED".equals(res)) {
                        refundBills.setStatus(PaypalResponseCode.getStatus(paymentStatus));
                        refundBills.setRefundTime(date.getTime());
                        refundBillsService.updateById(refundBills);
                        NotifyRequest notifyRequest = new NotifyRequest();
                        notifyRequest.setMerchantBillNo(refundBills.getMerchantBillNo());
                        notifyRequest.setMerchantRefundNo(refundBills.getMerchantRefundNo());
                        notifyRequest.setStatus(refundBills.getStatus());
                        notifyRequest.setMsg("退款回调");
                        notifyRequest.setType(1);
                        notifyRequest.setTransTime(refundBills.getRefundTime());//交易时间
                        outNotifyLog.setStatus(1);
                        outNotifyLog.setBillNo(refundBills.getRefundNo());//支付中台生产的uuid，如需要 在inbill表查询
                        //第三方退款回调日志记录
                        if (StringUtils.isNotEmpty(refundBills.getNotifyUrl())) {
                            notifyMerchantService.postMerchant(notifyRequest, refundBills.getNotifyUrl(), refundBills.getMerchantCode());//通知给商家
                        }
                    }
                    outNotifyLogService.insert(outNotifyLog);
                }else {//支付回调
                    InBills inBills = inBillsService.getBySaleId(txnId);
                    //TODO 记录主体回调数据
                    InNotifyLog inNotifyLog = new InNotifyLog();
                    inNotifyLog.setCallbackText(str);
                    inNotifyLog.setMerchantBillNo(inBills.getMerchantBillNo());
                    inNotifyLog.setStatus(0);
                    inNotifyLog.setTransNo(txnId);
                    if ("VERIFIED".equals(res)) {
                        inBills.setStatus(PaypalResponseCode.getStatus(paymentStatus));
                        inBills.setPayTime(date.getTime());
                        inBillsService.updateById(inBills);
                        NotifyRequest notifyRequest = new NotifyRequest();
                        notifyRequest.setMerchantBillNo(inBills.getMerchantBillNo());
                        notifyRequest.setBillNo(inBills.getBillNo());
                        notifyRequest.setStatus(inBills.getStatus());
                        notifyRequest.setMsg("支付回调");
                        notifyRequest.setType(1);
                        notifyRequest.setTransTime(inBills.getPayTime());//交易时间
                        inNotifyLog.setStatus(1);
                        inNotifyLog.setBillNo(inBills.getBillNo());//支付中台生产的uuid，如需要 在inbill表查询
                        if (StringUtils.isNotEmpty(inBills.getNotifyUrl())) {
                            notifyMerchantService.postMerchant(notifyRequest, inBills.getNotifyUrl(), inBills.getMerchantCode());//通知给商家
                        }
                    }
                    inNotifyLogService.insert(inNotifyLog);
                }
//            else if ("INVALID".equals(res)) {
//                //非法信息，可以将此记录到您的日志文件中以备调查
//            } else {
//                //处理其他错误
//            }
        }catch(Exception un){
            log.error("paypal异步通知:{}",un.getMessage());
        }
    }
}
